package org.boot.easyapi.core.framework.jdbc;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.*;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.sql.PreparedStatement;
import java.util.List;
import java.util.Map;


@Slf4j
@Component("easyapi-jdbcRepository")
public class JdbcRepository {

    @Resource
    private JdbcTemplate jdbcTemplate;


    /**
     * 插入并返回数据库生成的主键
     * @param sql  INSERT 语句
     * @param args 参数
     * @return 主键（自增）
     */
    @Transactional(rollbackFor = Exception.class)
    public Long insert(String sql, Object... args) {
        try{
            log.debug(">>> SQL:{}", sql);
            log.debug(">>> PARAMS:{}", JSONObject.toJSONString(args));
            KeyHolder keyHolder = new GeneratedKeyHolder();
            jdbcTemplate.update(connection -> {
                // 关键：只声明主键列，别用 Statement.RETURN_GENERATED_KEYS
                PreparedStatement ps = connection
                        .prepareStatement(sql, new String[]{"id"});
                for (int i = 0; i < args.length; i++) {
                    ps.setObject(i + 1, args[i]);
                }
                return ps;
            }, keyHolder);

            return keyHolder.getKey().longValue();   // 现在只会拿到 id
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public int update(String sql, PreparedStatementSetter pss) {
        try{
            log.debug(">>> SQL:{}", sql);
            log.debug(">>> PARAMS:{}", JSONObject.toJSONString(pss));
            return jdbcTemplate.update(sql, pss);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    @Transactional(rollbackFor = Exception.class)
    public int update(final PreparedStatementCreator psc, final KeyHolder generatedKeyHolder){
        try{
            log.debug(">>> SQL:{}", psc.toString());
            log.debug(">>> PARAMS:{}", JSONObject.toJSONString(psc));
            return jdbcTemplate.update(psc, generatedKeyHolder);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    @Transactional(rollbackFor = Exception.class)
    public int update(String sql, Object[] args) {
        try{
            log.debug(">>> SQL:{}", sql);
            log.debug(">>> PARAMS:{}", JSONObject.toJSONString(args));
            return jdbcTemplate.update(sql, args);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public int update(String sql, Object arg) {
        try{
            log.debug(">>> SQL:{}", sql);
            log.debug(">>> PARAMS:{}", JSONObject.toJSONString(arg));
            return jdbcTemplate.update(sql, arg);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public int[] batchUpdate(String sql, List<Object[]> batchArgs) {
        try{
            log.debug(">>> SQL:{}", sql);
            log.debug(">>> PARAMS:{}", JSONObject.toJSONString(batchArgs));
            return jdbcTemplate.batchUpdate(sql, batchArgs);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    public <T> T queryForObject(String sql, Class<T> requiredType, @Nullable Object... args) {
        try{
            log.debug(">>> SQL:{}", sql);
            log.debug(">>> PARAMS:{}", JSONObject.toJSONString(args));
            return jdbcTemplate.queryForObject(sql, requiredType, args);
        } catch (EmptyResultDataAccessException e){
//            log.error(">>> queryForObject ---> NULL ERROR:", e);
            return null;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public Map<String, Object> queryForMap(String sql, Object params) {
        try{
            log.debug(">>> SQL:{}", sql);
            log.debug(">>> PARAMS:{}", JSONObject.toJSONString(params));
            return jdbcTemplate.queryForMap(sql, params);
        } catch (EmptyResultDataAccessException e){
//            log.error(">>> queryForMap ---> NULL ERROR:", e);
            return null;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public Map<String, Object> queryForMap(String sql, Object[] params)  {
        try{
            log.debug(">>> SQL:[{}]", sql);
            log.debug(">>> PARAMS:{}", JSONObject.toJSONString(params));
            return jdbcTemplate.queryForMap(sql, params);
        } catch (EmptyResultDataAccessException e){
//            log.error(">>> queryForMap ---> NULL ERROR:", e);
            return null;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public List<Map<String, Object>> queryForList(String sql, Object[] params){
        try{
            log.debug(">>> SQL:[{}]", sql);
            log.debug(">>> PARAMS:{}", JSONObject.toJSONString(params));
            return jdbcTemplate.queryForList(sql, params);
        } catch (EmptyResultDataAccessException e){
//            log.error(">>> queryForList ---> NULL ERROR:", e);
            return null;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }





}
